/**
 *	\file CDSQStateMaintainer.cpp
 *  
 *  \section desc_sec Description
 *  Description
 *  
 *  \if copyright
 *  	Copyright 2008 Jeffrey Zhang. All rights reserved.
 *  \endif
 *  
 *  \section hist_sec Change History:
 *  	-	2/11/08		Jeffrey Zhang		Created this file
 *  
 *  <hr>
 *  \addtogroup AKCPP
 *  @{
 */
#include "AKCommon.h"
#include "CDSQStateMaintainer.h"

CDSQStateMaintainer::CDSQStateMaintainer(unsigned int aNumOfSides)
:AKCPPStateMaintainer(aNumOfSides)
{
	winners = new vector<AKSideIndex>();
	reachableCells = new vector<AKCellIndex>();
	somePieces	= new vector<AKPiece>();
	allPieces	= new vector<AKPiece>();
	movedPieces	= new vector<AKMove>();
}

CDSQStateMaintainer::~CDSQStateMaintainer(void)
{
	delete winners;
	delete reachableCells;
	delete somePieces;
	delete allPieces;
	delete movedPieces;
}

void CDSQStateMaintainer::backToInitialState()
{
	memcpy(CurPosition, InitChessBoard, 63*sizeof(PieceIndex));
	
	lastMove = AKZeroMove;
	nextPlayer = Left;
}

bool CDSQStateMaintainer::performMove(const AKMove newMove)
{
	PieceMove cmMove;
	int i,j,x,y;
	
	x = newMove.srcCell%9;
	y = newMove.srcCell/9;
	
	i = newMove.dstCell/9;
	j = newMove.dstCell%9;
	
	if( !IsValidMove(CurPosition, x, y, j, i) )
		return false;
	else
	{
		cmMove.From.x = x;
		cmMove.From.y = y;
		cmMove.To.x = j;
		cmMove.To.y = i;
	}
	
	undotype = MakeMove(&cmMove);
	
	lastMove = newMove;
	nextPlayer = (nextPlayer == Left) ? Right : Left;
	
	return true;
}

bool CDSQStateMaintainer::canUndo()
{
	if (lastMove.piece != NSNotFound && lastMove.dstCell != NSNotFound)
		return true;
	else
		return false;
}
bool CDSQStateMaintainer::undo()
{
	if (canUndo())
	{
		// Only one time of undo is supported here.
		PieceMove cmMove;
		
		cmMove.From.x = lastMove.srcCell%9;
		cmMove.From.y = lastMove.srcCell/9;
		cmMove.To.y = lastMove.dstCell/9;
		cmMove.To.x = lastMove.dstCell%9;
		
		UnMakeMove(&cmMove,undotype);
		lastMove = AKZeroMove;
		return true;
	}
	else
		return false;
}

const AKSideIndex CDSQStateMaintainer::getNextPlayerSide()
{
	return nextPlayer;
}

// Piece information
const vector<AKPiece>* CDSQStateMaintainer::getPlaceablePiecesAtCell(const AKCellIndex cellIndex)
{
	int i,j,x,y;
	x = cellIndex%9;
	y = cellIndex/9;
	
	AKPiece aPiece;
	
	somePieces->clear();
	
	for( i = 0;i<7;i++ )
		for( j = 0;j<9;j++ )
		{
			if( nextPlayer == GetSide(CurPosition[i][j]) )
			{
				if( IsValidMove(CurPosition, j, i, x, y ) )
				{
					aPiece.side  = nextPlayer;
					aPiece.piece = CurPosition[i][j];
					aPiece.cell  = i*9+j;
					
					somePieces->insert(somePieces->begin(), aPiece);
				}
			}
		}
	
	return somePieces;
}

const vector<AKCellIndex>* CDSQStateMaintainer::getReachableCellsForPiece(const AKPiece piece)
{
	int i,j,x,y;
	
	reachableCells->clear();
	
	i = piece.cell/9;
	j = piece.cell%9;
	
	x = j-1;
	y = i;
	if( x>=0 && IsValidMove(CurPosition, j, i, x, y) )
		reachableCells->push_back(y*9+x);
	
	x = j+1;
	y = i;
	if( x<9 && IsValidMove(CurPosition, j, i, x, y) )
		reachableCells->push_back(y*9+x);
	
	x = j;
	y = i-1;
	if( y>=0 && IsValidMove(CurPosition, j, i, x, y) )
		reachableCells->push_back(y*9+x);
	
	x = j;
	y = i+1;
	if( y<7 && IsValidMove(CurPosition, j, i, x, y) )
		reachableCells->push_back(y*9+x);
	
	if(piece.piece == LTiger || piece.piece == LLion || piece.piece == RTiger || piece.piece == RLion )
	{
		x = j-4;
		y = i;
		if( x>=0 && CurPosition[i][j-1]==Water && IsValidMove(CurPosition, j, i, x, y) )
			reachableCells->push_back(y*9+x);
		
		x = j+4;
		y = i;
		if( x<9 && CurPosition[i][j+1]==Water && IsValidMove(CurPosition, j, i, x, y) )
			reachableCells->push_back(y*9+x);
		
		x = j;
		y = i-3;
		if( y>=0 && CurPosition[i-1][j]==Water && IsValidMove(CurPosition, j, i, x, y) )
			reachableCells->push_back(y*9+x);
		
		x = j;
		y = i+3;
		if( y<7 && CurPosition[i+1][j]==Water && IsValidMove(CurPosition, j, i, x, y) )
			reachableCells->push_back(y*9+x);
	}
	return reachableCells;
}

const vector<AKPiece>*	CDSQStateMaintainer::getCreatedPieces()
{
	//DSQ never create a piece...
	somePieces->clear();
	return somePieces;
}

const vector<AKPiece>*	CDSQStateMaintainer::getElminatedPieces()
{
	somePieces->clear();
	
	if( !IsOther(undotype) )
	{
		AKPiece piece;
		piece.piece = undotype;
		piece.cell  = lastMove.dstCell;
		piece.side  = !nextPlayer;
		
		somePieces->push_back(piece);
	}
	return somePieces;
}

const vector<AKMove>*	CDSQStateMaintainer::getMovedPieces()
{
	movedPieces->clear();
	
	//lastMove.side == !nextPlayer??
	movedPieces->push_back(lastMove);
	
	return movedPieces;
}

const vector<AKPiece>*	CDSQStateMaintainer::getAllPieces()
{
	int i,j;
	AKPiece piece;
	
	allPieces->clear();
	for (i = 0; i < 7; i++)
		for(j = 0;j < 9; j++)
		{
			if ( !IsOther(CurPosition[i][j]) )
			{
				piece.piece = CurPosition[i][j];
				piece.side = GetSide(CurPosition[i][j]);
				piece.cell = i*9+j;
				allPieces->push_back(piece);
			}
		}
	
	return allPieces;
}

const vector<AKSideIndex>*	CDSQStateMaintainer::getWinners()
{
	AKSideIndex retVal = NSNotFound;
	
	if( CurPosition[3][0]!=LHome )
		retVal = Right;
	else if( CurPosition[3][8]!=RHome )
		retVal = Left;
	
	winners->clear();
	winners->push_back(retVal);
	
	return winners;
}

const AKMove CDSQStateMaintainer::getAlternativeMoveForNextPlayer()
{
	//for what?
	return AKZeroMove;
}
/***********************************************************/

int CDSQStateMaintainer::IsValidMove(PieceIndex position[][9], int nFromX, int nFromY, int nToX, int nToY)
{
	int nMoveChessID, nTargetID;
	
	if( position[nFromY][nFromX] == NOCHESS || IsOther(position[nFromY][nFromX]) )
		return false; //check the source
	
	if( !(nFromX == nToX || nFromY == nToY) )
		return false; //can not run across
	
	if (nFromY ==  nToY && nFromX == nToX)
		return false;   //the destination as same as the source
	
	nMoveChessID = position[nFromY][nFromX];
	nTargetID = position[nToY][nToX];
	
	if( nTargetID==LHome && IsLeft(nMoveChessID) )
		return false;
	if( nTargetID==RHome && IsRight(nMoveChessID) )
		return false;
	if (IsSameSide(nMoveChessID, nTargetID))
		return false;   //can not defeat the own-side chess.
    if( nTargetID == Water && (nMoveChessID!=LMice && nMoveChessID!=RMice) )
        return false;   //only mouse can dive into water
	
	//check the type of the source-chess
	
	//only the tiger and the lion can jump
	if( !(nMoveChessID == LTiger || nMoveChessID == LLion || nMoveChessID == RTiger || nMoveChessID == RLion) )
	{
		if( !(abs(nFromX-nToX)==1 || abs(nFromY-nToY)==1) )
			return false;
	}
	//must be tiger or lion
	else
	{
		//move vertically
		if(nFromX==nToX)
		{
			//is jump?
			if( abs(nFromY-nToY)>1 )
			{
				//move up
				if( nFromY>nToY )
				{
					//in case that the mouse in the water...
					if( position[nFromY-1][nFromX]!=Water || position[nFromY-2][nFromX]!=Water )
						return false;
				}
				//move down
				else// assert( nFromY<nToY )
				{
					if( position[nFromY+1][nFromX]!=Water || position[nFromY+2][nFromX]!=Water )
						return false;
				}
			}
		}
		//move horizontally
		else if(nFromY==nToY)
		{
			//is jump?
			if( abs(nFromX-nToX)>1 )
			{
				//move left
				if( nFromX>nToX )
				{
					if( position[nFromY][nFromX-1]!=Water || position[nFromY][nFromX-2]!=Water || position[nFromY][nFromX-3]!=Water )
						return false;
				}
				//move right
				else// assert( nFromX<nToX )
				{
					if( position[nFromY][nFromX+1]!=Water || position[nFromY][nFromX+2]!=Water || position[nFromY][nFromX+3]!=Water )
						return false;
				}
			}
		}
		else
			return false;
	}
	
	//check the desternation-chess
	if( nTargetID && !IsOther(nTargetID) )
	{
		//source-chess is right-side
		if ( IsRight(nMoveChessID) )
		{
			//the elephants only can eat the mouse when the latter in trap
			if( (nMoveChessID==16 && nTargetID==1) && (InitChessBoard[nToY][nToX] != Trap) )
				return false;
			
			//mouse eat elephants
			if( nMoveChessID==9 && nTargetID==8 )
			{
				if(InitChessBoard[nFromY][nFromX] == Water) //mouse can not be in the water
					return false;
				else
					return true;
			}
			if( InitChessBoard[nToY][nToX] != Trap && nMoveChessID-8 < nTargetID )
				return false;//source-chess can not eat the desternation-chess
			
			if(InitChessBoard[nToY][nToX] == Water) //can not eat the mouse which are in the water
				return false;
		}
		else //assert( IsLeft(nMoveChessID) )
		{
			if( (nMoveChessID==8 && nTargetID==9) && (InitChessBoard[nToY][nToX] != Trap) )
				return false;
			if( nMoveChessID==1 && nTargetID==16 )
			{
				if(InitChessBoard[nFromY][nFromX] == Water)
					return false;
				else
					return true;
			}
			if( InitChessBoard[nToY][nToX] != Trap && nMoveChessID < nTargetID-8 )
				return false;
			
			if(InitChessBoard[nToY][nToX] == Water)
				return false;
		}
	}
	
	//include the case which the TargetID is HOME
	return true;
}

unsigned int CDSQStateMaintainer::MakeMove(PieceMove* move)
{
	unsigned int nChessID;
	//save the TargetID for redo
	nChessID = CurPosition[move->To.y][move->To.x];
	//execute the move
	CurPosition[move->To.y][move->To.x] = CurPosition[move->From.y][move->From.x];
	//recover the source-grid
	if( !IsOther(InitChessBoard[move->From.y][move->From.x]) )
		CurPosition[move->From.y][move->From.x] = NOCHESS;
	else
		CurPosition[move->From.y][move->From.x] = InitChessBoard[move->From.y][move->From.x];
	
	return nChessID;
}

void CDSQStateMaintainer::UnMakeMove(PieceMove* move,unsigned int nChessID)
{
	//redo
	CurPosition[move->From.y][move->From.x] = CurPosition[move->To.y][move->To.x];
	CurPosition[move->To.y][move->To.x] = nChessID;
}

/** @} */
